package com.pxx.search.service;


import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.item.common.PageResult;
import com.pxx.item.pojo.*;
import com.pxx.search.client.BrandClient;
import com.pxx.search.client.CategoryClient;
import com.pxx.search.client.GoodsClient;
import com.pxx.search.client.SpecClient;
import com.pxx.search.pojo.GoodsEs;
import com.pxx.search.pojo.SearchRequest;
import com.pxx.search.pojo.SearchResult;
import com.pxx.search.respsitory.GoodsRespsitory;
import org.apache.commons.lang.StringUtils;

import org.apache.commons.lang.math.NumberUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;


import java.util.*;

@Service
public class SearchService {
    @Autowired
    private BrandClient brandClient;

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private GoodsClient goodsClient;
    @Autowired
    private SpecClient specClient;

    @Autowired
    GoodsRespsitory goodsRespsitory;


    public static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     *
     * 构建新增的数据
     * @param Spu
     * @return
     */
    public GoodsEs buildGoodsEs(TbSpu Spu) throws Exception {

        //创建一个新的对象
        GoodsEs goodsEs = new GoodsEs();


        //查询品牌的数据
        Brand brandInfo = brandClient.getBrandInfo(Spu.getBrandId());
        //查询分类的名称
        List<String> names = categoryClient.queryNamesByIds(Arrays.asList(Spu.getCid1(), Spu.getCid2(), Spu.getCid3()));
        //查询spu下面所有的skus
        List<Sku> skus = goodsClient.querySkuListBySpuId(Spu.getId());
        //存放skus中所有的价格
        List<Long> prices = new ArrayList<Long>();
        //存放 skulist的数据
        List<Map<String,Object>> skuMapList =   new ArrayList<Map<String,Object>>();
        //遍历skus，获取价格集合
        skus.forEach(sku -> {
            prices.add(sku.getPrice());

            //创建一个map,用来存放sku中的数据，用来转成json数据
            Map<String,Object> map =    new HashMap<String,Object>();
            map.put("id",sku.getId());
            map.put("title",sku.getTitle());
            map.put("price",sku.getPrice());
            map.put("image", StringUtils.isNotBlank(sku.getImages())?StringUtils.split(sku.getImages(),",")[0]:"");

            skuMapList.add(map);
        });

        //查询规格
        //获取所有的规格参数
        List<SpecParam> params = specClient.queryParams(null, Spu.getCid3(), null, true);

        //查询spu下的spuDetail的数据
        TbSpuDetail tbSpuDetail = goodsClient.querySpuDetailBySpuId(Spu.getId());
        //通用类型的key 和value都拿到
        Map<Long,Object>  genericSpecMap= MAPPER.readValue(tbSpuDetail.getGenericSpec(), new TypeReference<Map<Long, Object>>() {
        });
        //特有类型的key 和value都拿到
        Map<Long,List<Object>>  specialSpecMap= MAPPER.readValue(tbSpuDetail.getSpecialSpec(), new TypeReference<Map<Long, List<Object>>>() {
        });


        //定义map接受 {规格参数名，规格参数值}
        Map<String,Object> paramMap = new HashMap<String,Object>();
        for (SpecParam param : params) {
            //判断是否是通用属性
            if (param.getGeneric()){
                String value =genericSpecMap.get(param.getId()).toString();
                //是否是数值类型
                if (param.getNumeric()){
                    //判断如果是数值，判断该数值在哪个区间
                    value =chooesSegmnt(value,param);
                }
                //把参数名和值放入到结果集合中
                paramMap.put(param.getName(),value);
            }else{
                paramMap.put(param.getName(),specialSpecMap.get(param.getId()));
            }
        }



        //设置参数
        goodsEs.setId(Spu.getId());
        goodsEs.setCid1(Spu.getCid1());
        goodsEs.setCid2(Spu.getCid2());
        goodsEs.setCid3(Spu.getCid3());
        goodsEs.setBrandId(Spu.getBrandId());
        goodsEs.setCreateTime(Spu.getCreateTime());
        goodsEs.setSubTitle(Spu.getSubTitle());


        goodsEs.setAll(Spu.getTitle()+brandInfo.getName()+StringUtils.join(names," "));
        goodsEs.setPrice(prices);
        goodsEs.setSkus(MAPPER.writeValueAsString(skuMapList));
        goodsEs.setSpecs(paramMap);



        return goodsEs;
    }


    /**
     * 判断数值在哪个区间
     */

    public String chooesSegmnt(String value,SpecParam p){
        double   val = NumberUtils.toDouble(value);
        String reslut = "其他";

        for (String s : p.getSegments().split(",")) {
            String[] segs = s.split("-");
            //获取取值范围
            double begin = NumberUtils.toDouble(segs[0]);
            double end =  Double.MAX_VALUE ;
            if (segs.length==2){
                end = NumberUtils.toDouble(segs[1]);
            }
            //判断是否在范围内
            if (val>=begin && val<end){
                if (segs.length==1){
                    reslut = segs[0]+p.getUnit()+"以上";
                }else if (begin==0){
                    reslut = segs[1]+p.getUnit()+"以下";
                }else{
                    reslut = s+p.getUnit();
                }
                break;
            }

        }

        return reslut;
    }


    public PageResult<GoodsEs> search(SearchRequest searchRequest) {
        String key = searchRequest.getKey();
        if (StringUtils.isEmpty(key)){
            return null;
        }
        //编写构造的查询语句
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        BoolQueryBuilder boolQueryBuilder = buildBoolQueryBuilder(searchRequest);


        queryBuilder.withQuery(boolQueryBuilder);
        //设置分页的参数
        Integer page = searchRequest.getPage(); // 1
        Integer size = searchRequest.getSize(); //20
        queryBuilder.withPageable(PageRequest.of(page-1,size));

        //排序
        String sortBy = searchRequest.getSortBy();
        Boolean descending = searchRequest.getDescending();
        if (StringUtils.isNotEmpty(sortBy)){
            queryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(descending? SortOrder.DESC:SortOrder.ASC));
        }


        //进行查询。获取到结果，封装进行返回
        queryBuilder.withSourceFilter(new FetchSourceFilter(new  String[]{"id","skus","subTitle"},null));
/***************************聚合************************************************************************/
        String categoryAggName= "categories" ;
        String brandAggName = "brands";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));
/***************************聚合************************************************************************/

//        Page<GoodsEs> search = goodsRespsitory.search(queryBuilder.build());
        AggregatedPage<GoodsEs> search = (AggregatedPage<GoodsEs>) goodsRespsitory.search(queryBuilder.build());
        //获取商品分类的数据吧
        List<Map<String,Object>> categories = getCategoryAggResult(search.getAggregation(categoryAggName));
        //获取品牌名称的数据
        List<Brand> brands = getBrandAggResult(search.getAggregation(brandAggName));
/***************************聚合************规格参数的聚合************************************************************/
        //判断分类聚合的结果集大小 ，如果等于1 则聚合
        List<Map<String,Object>> specs = null;
        if (categories.size()==1){
            specs = getParamAggResult((Long) categories.get(0).get("id"),boolQueryBuilder);
        }
/****************************************************************************************************************/




        //计算总条数
        Long total = search.getTotalElements();
        // 180条数据 每页20条 问 一共多少页 = 9 页
        //187条数 每页20条 问一共多少页 10页
        int totalPage =   (total.intValue()+size-1)/size;

        return new SearchResult(search.getContent(),total,totalPage,categories,brands,specs);

    }

    /**
     * 构造编译条件
     * @param searchRequest
     * @return
     */
    private BoolQueryBuilder buildBoolQueryBuilder(SearchRequest searchRequest) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //添加基本条件
        boolQueryBuilder.must(QueryBuilders.matchQuery("all",searchRequest.getKey()).operator(Operator.AND));

        //添加过滤条件
        if (CollectionUtils.isEmpty(searchRequest.getFilter())){
            return boolQueryBuilder;
        }

        for (Map.Entry<String, String> entry : searchRequest.getFilter().entrySet()) {
            String key = entry.getKey();
            //如果过滤条件中是 品牌 过滤的字段是brandId
            if (StringUtils.equals("品牌",key)){
                key = "brandId";
            }else if (StringUtils.equals("分类",key)){
                key="cid3";
            }else {
                key="specs."+key+".keyword" ;
            }
            boolQueryBuilder.filter(QueryBuilders.termQuery(key,entry.getValue()));
        }


        return boolQueryBuilder;
    }

    /**
     * 获取规格参数聚合
     * @param id
     * @param basicQuery
     * @return
     */
    private List<Map<String, Object>> getParamAggResult(Long id, BoolQueryBuilder basicQuery) {
        //创建自定义查询构造器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //基于基本的查询条件， 聚合规格参数
        queryBuilder.withQuery(basicQuery);
        //查询要聚合的规格参数去数据库中查询
        List<SpecParam> specParams = specClient.queryParams(null, id, null, true);

        //添加聚合
        specParams.forEach(param->{
            queryBuilder.addAggregation(AggregationBuilders.terms(param.getName()).field("specs."+param.getName()+".keyword"));
        });
        //只需要聚合的结果，不需要查询的结果
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{},null));

        //执行聚合查询
        AggregatedPage<GoodsEs> goodsPage = (AggregatedPage<GoodsEs>) goodsRespsitory.search(queryBuilder.build());

        //定义一个集合，聚合的结果集
        List<Map<String, Object>> paramMapList  =  new ArrayList<>();
        //解析数据
        Map<String, Aggregation> aggregationMap = goodsPage.getAggregations().asMap();
        for (Map.Entry<String, Aggregation> entry : aggregationMap.entrySet()) {
            Map<String, Object> map = new HashMap<>();
            //放入规格参名称
            map.put("k",entry.getKey());
            //收集规格参数值
            List<Object> options =  new ArrayList<>();
            //解析每一个聚合
            StringTerms terms = (StringTerms) entry.getValue();
            //遍历每一个桶内的数据，把key放入收集规格参数集合中
            terms.getBuckets().forEach(bucket -> options.add(bucket.getKeyAsString()));
            map.put("options",options);
            paramMapList.add(map);
        }

        return paramMapList;
    }

    /**
     * 获取聚合中的商品品牌的名称
     * @param aggregation
     * @return
     */
    private List<Brand> getBrandAggResult(Aggregation aggregation) {
        LongTerms terms = (LongTerms) aggregation;
        //获取所有的分类的id桶
        List<LongTerms.Bucket> buckets = terms.getBuckets();
        List<Brand> brands =  new ArrayList<Brand>();
        buckets.forEach(bucket -> {
            long brandId = bucket.getKeyAsNumber().longValue();
            //因为是对象，所以我们需要去数据库中查询当前对象的数据
            Brand brandInfo = brandClient.getBrandInfo(brandId);
            brands.add(brandInfo);
        });

        return brands;
    }

    private List<Map<String, Object>> getCategoryAggResult(Aggregation aggregation) {
        LongTerms terms = (LongTerms) aggregation;
        //获取所有的分类的id桶
        List<LongTerms.Bucket> buckets = terms.getBuckets();

        List<Map<String, Object>> categories =  new ArrayList<>();
        List<Long> cids =   new ArrayList<Long>();
        //解析所有的id桶，查询商品分类对象
        buckets.forEach(bucket -> {
            cids.add(bucket.getKeyAsNumber().longValue()) ;
        });

        if (!CollectionUtils.isEmpty(cids)){

            List<String> names = categoryClient.queryNamesByIds(cids);
            for (int i = 0; i < cids.size(); i++) {
                Map<String, Object> map =new HashMap<>();
                map.put("id",cids.get(i));
                map.put("name",names.get(i));
                categories.add(map);

            }
        }
        return categories;
    }
}
